/*
* Decoder for the strings of LatentBot
* CC-BY: hasherezade
*/

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#ifdef _MSC_VER
    #include <stdint.h>
#else
    #include <inttypes.h>
#endif

#ifndef BYTE
typedef unsigned char BYTE;
#endif

#ifndef WORD
   typedef uint16_t WORD; 
#endif

#ifndef DWORD
   typedef uint32_t DWORD; 
#endif

const size_t modifiers_count = 4;
const WORD modifiers[modifiers_count] = { 0xBB8, 0x2328, 0x264d, 0x1918 };
const char *modifiers_info[modifiers_count] = { "hardcoded", "registry", "sent", "received" };

const size_t lookup_size = 0xFF;
BYTE lookup_table[lookup_size] = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3E\x00\x00\x00\x3F\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x00\x00\x00\x00\x00\x00\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";


void xor_dec(BYTE* data, size_t data_size, WORD modifier)
{
    BYTE result[data_size] = {0}; 
    size_t i = 0;
    for (i = 0; i < data_size; i++) {
        result[i] = (data[i] ^ (modifier >> 8));
        modifier += data[i];
        modifier *= 0x0CE6D;
        modifier += 0x058BF;
    }

    //delete non-printable characters at the end:
    for (i = data_size - 1; i != 0; i--) {
        if (!isprint(result[i])) {
            result[i] = 0;
        }
    }
    printf("%s\n", result);
}

DWORD transform_chunk(BYTE* chunk, size_t chunk_size, BYTE *lookup_table)
{
    if (chunk_size > 4) chunk_size = 4;

    DWORD result = 0;

    for (int i = 0; i < chunk_size; i++) {
        DWORD val = lookup_table[chunk[i]];
        val =  val << (0x6 *i);
        result = result + val;
    }

    return result;
}

/*
Example:
type: 0
Gh7kOXLlDCJD/NnXBnCB7Vm8xpnInLlIk3pAuDYkY/MgfvJxQIJwudvt3Pg9MFdwye8nMY2WEYb4WsUn14Rd
*/

bool decode_line(char *in_str, int modifier)
{
    size_t enc_len = strlen(in_str);
    size_t chunks_num = enc_len / 4;
    chunks_num += (enc_len > chunks_num) ? 1: 0; //round up

    size_t out_buf_size = chunks_num * 4;

    BYTE *out_buf = new BYTE[out_buf_size];
    memset(out_buf, 0, out_buf_size);

    BYTE* out_buf_ptr = out_buf;

    for (int i = 0; i < chunks_num; i++) {    
        DWORD res = transform_chunk((BYTE*)(in_str + 4 * i), 4, lookup_table);
        memcpy(out_buf_ptr, &res, 3);
        out_buf_ptr += 3;
    }

    size_t out_len = (out_buf_ptr - out_buf);
    //decodes and pritns:
    xor_dec(out_buf, out_len, modifier);

    delete []out_buf;
    out_buf = NULL;
    return true;
}

int read_next(int modifier)
{
    const int max_n = 0x1000;
    char in_buffer[max_n] = {0};
    char *result = NULL;

    while (true) {
        result = fgets(in_buffer, max_n, stdin);
        if (result == NULL) break;

        decode_line(in_buffer, modifier);

        if (feof (stdin)) break;
    }
    return 0;
}

int main(int argc, char *argv[])
{
    if (argc < 2) {
        printf("params: <type> <encrypted string>\n");
        printf("available types:\n");
        for (size_t i = 0; i < modifiers_count; i++) {
            printf("%d: %s\n", i, modifiers_info[i]);
        }
        return 0;
    }

    size_t modif_id = (argv[1][0] - '0') % modifiers_count;
    int modifier = modifiers[modif_id];

    if (argc < 3) {
        read_next(modifier);
    } else {
        char *in_str = argv[2];
        decode_line(in_str, modifier);
    }
    return 0;
}
